package com.lry.basic.netty.demo2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.*;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.io.UnsupportedEncodingException;
import java.util.EventListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/9/11
 */
public class Server {
    private int port;
    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    public Server(int port){
        this.port = port;
    }
    public static void main(String[] args) {
        new Server(9999).start();
    }
    private void start(){
        //接收连接请求线程
        EventLoopGroup boss = new NioEventLoopGroup(1);
        //工作线程组
        EventLoopGroup worker = new NioEventLoopGroup();
        try{
            ServerBootstrap server = new ServerBootstrap();
            server.group(boss,worker);
            server.channel(NioServerSocketChannel.class);
            server.childHandler(new ChannelInitializer<SocketChannel>(){
                @Override
                protected void initChannel(SocketChannel socketChannel) throws UnsupportedEncodingException {
                    System.out.println("client conn：" + socketChannel.remoteAddress());
                    ChannelPipeline pipeline = socketChannel.pipeline();
//                    ByteBuf delimiter = Unpooled.copiedBuffer("$".getBytes("UTF-8"));
//                    pipeline.addLast("frame",new DelimiterBasedFrameDecoder(2048,delimiter));
//                    pipeline.addLast(new LineBasedFrameDecoder(100));
                    //this(maxFrameLength, lengthFieldOffset, lengthFieldLength,lengthAdjustment,initialBytesToStrip);
                    pipeline.addLast(new LengthFieldBasedFrameDecoder(1024,0,4,0,4));
//                    socketChannel.pipeline().addLast( new LengthFieldPrepender(4,false));
                    pipeline.addLast("decode",new StringDecoder());//解码器
                    pipeline.addLast("encode",new StringEncoder());
                    pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                        //每当从客户端有消息写入时
                        @Override
                        protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
                            Channel inComing = ctx.channel();
                            System.out.println("[" + inComing.remoteAddress() + "]: "+s);
                            for (Channel channel : channels){
                                if (channel != inComing){
                                    channel.writeAndFlush("[用户" + inComing.remoteAddress() + " 说：]" + s + "\n");
                                }else {
                                    channel.writeAndFlush("[我说：]" + s + "\n");
                                }
                            }
                        }
                        //当有客户端连接时，handlerAdded会执行,就把该客户端的通道记录下来，加入队列
                        @Override
                        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
                            Channel inComing = ctx.channel();//获得客户端通道
                            //通知其他客户端有新人进入
                            for (Channel channel : channels){
                                if (channel != inComing)
                                    channel.writeAndFlush("[欢迎: " + inComing.remoteAddress() + "] 进入聊天室！\n");
                            }
                            channels.add(inComing);//加入队列
                        }
                        //客户端断开连接
                        @Override
                        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
                            Channel outComing = ctx.channel();//获得客户端通道
                            //通知其他客户端有人离开
                            for (Channel channel : channels){
                                if (channel != outComing)
                                    channel.writeAndFlush("[再见: ]" + outComing.remoteAddress() + " 离开聊天室！\n");
                            }
                            channels.remove(outComing);
                        }
                        //在线
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            Channel inComing = ctx.channel();
                            System.out.println("[" + inComing.remoteAddress() + "]: 在线");
                        }

                        //离线
                        @Override
                        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                            Channel inComing = ctx.channel();
                            System.out.println("[" + inComing.remoteAddress() + "]: 离线");
                        }

                        @Override
                        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                            Channel inComing = ctx.channel();
                            System.out.println(inComing.remoteAddress() + "通讯异常！");
                            ctx.close();
                        }
                    });
                }
            });
            //配置NioServerSocketChannel的TCP参数
            server.option(ChannelOption.SO_BACKLOG, 1024);
            server.option(ChannelOption.SO_KEEPALIVE,true);
            ChannelFuture future =server.bind(port).sync();
            System.out.println("server start...");
            //使用sync方法进行阻塞，等待服务端链路关闭之后Main函数才退出
            future.channel().closeFuture().sync();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
